///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_RASTERIZER_CSCANLINERASTERIZER_H_
#define _BUOLA_IMAGE_RASTERIZER_CSCANLINERASTERIZER_H_

#include <buola/image/drawing/basic.h>
#include <buola/image/rasterizer/ccellrasterizer.h>
#include <buola/image/rasterizer/cscanlines.h>

namespace buola { namespace img {

template<class T>
inline unsigned clipping_flags(T x, T y, const CRect<T>& clip_box)
{
    return  (x > clip_box.r) |
            ((y > clip_box.b) << 1) |
            ((x < clip_box.l) << 2) |
            ((y < clip_box.t) << 3);
}

//--------------------------------------------------------clipping_flags_x
template<class T>
inline unsigned clipping_flags_x(T x, const CRect<T>& clip_box)
{
    return  (x > clip_box.r) | ((x < clip_box.l) << 2);
}


//--------------------------------------------------------clipping_flags_y
template<class T>
inline unsigned clipping_flags_y(T y, const CRect<T>& clip_box)
{
    return ((y > clip_box.b) << 1) | ((y < clip_box.t) << 3);
}

    

static const int cPolyMaxCoord=(1<<30)-1;

//------------------------------------------------------------ras_conv_int
struct ras_conv_int
{
    typedef int TCoord;
    static int mul_div(double a, double b, double c)
    {
        return (a * b / c); ///\todo iround
    }
    static int xi(int v) { return v; }
    static int yi(int v) { return v; }
    static int upscale(double v) { return (v * cPolySubpixelScale); } ///\todo iround
    static int downscale(int v)  { return v; }
};
#if 0
//--------------------------------------------------------ras_conv_int_sat
struct ras_conv_int_sat
{
    typedef int TCoord;
    static int mul_div(double a, double b, double c)
    {
        return saturation<poly_max_coord>::iround(a * b / c);
    }
    static int xi(int v) { return v; }
    static int yi(int v) { return v; }
    static int upscale(double v)
    {
        return saturation<poly_max_coord>::iround(v * poly_subpixel_scale);
    }
    static int downscale(int v) { return v; }
};

//---------------------------------------------------------ras_conv_int_3x
struct ras_conv_int_3x
{
    typedef int TCoord;
    static int mul_div(double a, double b, double c)
    {
        return iround(a * b / c);
    }
    static int xi(int v) { return v * 3; }
    static int yi(int v) { return v; }
    static int upscale(double v) { return iround(v * poly_subpixel_scale); }
    static int downscale(int v)  { return v; }
};

//-----------------------------------------------------------ras_conv_dbl
struct ras_conv_dbl
{
    typedef double TCoord;
    static double mul_div(double a, double b, double c)
    {
        return a * b / c;
    }
    static int xi(double v) { return iround(v * poly_subpixel_scale); }
    static int yi(double v) { return iround(v * poly_subpixel_scale); }
    static double upscale(double v) { return v; }
    static double downscale(int v)  { return v / double(poly_subpixel_scale); }
};

//--------------------------------------------------------ras_conv_dbl_3x
struct ras_conv_dbl_3x
{
    typedef double TCoord;
    static double mul_div(double a, double b, double c)
    {
        return a * b / c;
    }
    static int xi(double v) { return iround(v * poly_subpixel_scale * 3); }
    static int yi(double v) { return iround(v * poly_subpixel_scale); }
    static double upscale(double v) { return v; }
    static double downscale(int v)  { return v / double(poly_subpixel_scale); }
};
#endif
//------------------------------------------------------rasterizer_sl_clip
template<class tConv>
class CRasterizerSLClip
{
public:
    typedef tConv                      TConv;
    typedef typename tConv::TCoord TCoord;
    typedef CRect<TCoord>     TRect;

    //--------------------------------------------------------------------
    CRasterizerSLClip()
        :   mClipBox(0,0,0,0)
        ,   mX1(0)
        ,   mY1(0)
        ,   mF1(0)
        ,   mClipping(false)
    {}

    //--------------------------------------------------------------------
    void ResetClipping()
    {
        mClipping = false;
    }

    //--------------------------------------------------------------------
    void ClipBox(TCoord x1, TCoord y1, TCoord x2, TCoord y2)
    {
        mClipBox = TRect(x1, y1, x2, y2);
        mClipBox.Normalize();
        mClipping = true;
    }

    //--------------------------------------------------------------------
    void MoveTo(TCoord x1, TCoord y1)
    {
        mX1 = x1;
        mY1 = y1;
        if(mClipping) mF1 = clipping_flags(x1, y1, mClipBox);
    }

private:
    //------------------------------------------------------------------------
    template<class Rasterizer>
    void LineClipY(Rasterizer& ras,
                                TCoord x1, TCoord y1,
                                TCoord x2, TCoord y2,
                                unsigned   f1, unsigned   f2) const
    {
        f1 &= 10;
        f2 &= 10;
        if((f1 | f2) == 0)
        {
            // Fully visible
            ras.Line(tConv::xi(x1), tConv::yi(y1), tConv::xi(x2), tConv::yi(y2));
        }
        else
        {
            if(f1 == f2)
            {
                // Invisible by Y
                return;
            }

            TCoord tx1 = x1;
            TCoord ty1 = y1;
            TCoord tx2 = x2;
            TCoord ty2 = y2;

            if(f1 & 8) // y1 < clip.t
            {
                tx1 = x1 + tConv::mul_div(mClipBox.t-y1, x2-x1, y2-y1);
                ty1 = mClipBox.t;
            }

            if(f1 & 2) // y1 > clip.b
            {
                tx1 = x1 + tConv::mul_div(mClipBox.b-y1, x2-x1, y2-y1);
                ty1 = mClipBox.b;
            }

            if(f2 & 8) // y2 < clip.t
            {
                tx2 = x1 + tConv::mul_div(mClipBox.t-y1, x2-x1, y2-y1);
                ty2 = mClipBox.t;
            }

            if(f2 & 2) // y2 > clip.b
            {
                tx2 = x1 + tConv::mul_div(mClipBox.b-y1, x2-x1, y2-y1);
                ty2 = mClipBox.b;
            }
            ras.Line(tConv::xi(tx1), tConv::yi(ty1),
                        tConv::xi(tx2), tConv::yi(ty2));
        }
    }


public:
    //--------------------------------------------------------------------
    template<class tRasterizer>
    void LineTo(tRasterizer& ras, TCoord x2, TCoord y2)
    {
        if(mClipping)
        {
            unsigned f2 = clipping_flags(x2, y2, mClipBox);

            if((mF1 & 10) == (f2 & 10) && (mF1 & 10) != 0)
            {
                // Invisible by Y
                mX1 = x2;
                mY1 = y2;
                mF1 = f2;
                return;
            }

            TCoord x1 = mX1;
            TCoord y1 = mY1;
            unsigned   f1 = mF1;
            TCoord y3, y4;
            unsigned   f3, f4;

            switch(((f1 & 5) << 1) | (f2 & 5))
            {
            case 0: // Visible by X
                LineClipY(ras, x1, y1, x2, y2, f1, f2);
                break;

            case 1: // x2 > clip.r
                y3 = y1 + tConv::mul_div(mClipBox.r-x1, y2-y1, x2-x1);
                f3 = clipping_flags_y(y3, mClipBox);
                LineClipY(ras, x1, y1, mClipBox.r, y3, f1, f3);
                LineClipY(ras, mClipBox.r, y3, mClipBox.r, y2, f3, f2);
                break;

            case 2: // x1 > clip.r
                y3 = y1 + tConv::mul_div(mClipBox.r-x1, y2-y1, x2-x1);
                f3 = clipping_flags_y(y3, mClipBox);
                LineClipY(ras, mClipBox.r, y1, mClipBox.r, y3, f1, f3);
                LineClipY(ras, mClipBox.r, y3, x2, y2, f3, f2);
                break;

            case 3: // x1 > clip.r && x2 > clip.r
                LineClipY(ras, mClipBox.r, y1, mClipBox.r, y2, f1, f2);
                break;

            case 4: // x2 < clip.l
                y3 = y1 + tConv::mul_div(mClipBox.l-x1, y2-y1, x2-x1);
                f3 = clipping_flags_y(y3, mClipBox);
                LineClipY(ras, x1, y1, mClipBox.l, y3, f1, f3);
                LineClipY(ras, mClipBox.l, y3, mClipBox.l, y2, f3, f2);
                break;

            case 6: // x1 > clip.r && x2 < clip.l
                y3 = y1 + tConv::mul_div(mClipBox.r-x1, y2-y1, x2-x1);
                y4 = y1 + tConv::mul_div(mClipBox.l-x1, y2-y1, x2-x1);
                f3 = clipping_flags_y(y3, mClipBox);
                f4 = clipping_flags_y(y4, mClipBox);
                LineClipY(ras, mClipBox.r, y1, mClipBox.r, y3, f1, f3);
                LineClipY(ras, mClipBox.r, y3, mClipBox.l, y4, f3, f4);
                LineClipY(ras, mClipBox.l, y4, mClipBox.l, y2, f4, f2);
                break;

            case 8: // x1 < clip.l
                y3 = y1 + tConv::mul_div(mClipBox.l-x1, y2-y1, x2-x1);
                f3 = clipping_flags_y(y3, mClipBox);
                LineClipY(ras, mClipBox.l, y1, mClipBox.l, y3, f1, f3);
                LineClipY(ras, mClipBox.l, y3, x2, y2, f3, f2);
                break;

            case 9:  // x1 < clip.l && x2 > clip.r
                y3 = y1 + tConv::mul_div(mClipBox.l-x1, y2-y1, x2-x1);
                y4 = y1 + tConv::mul_div(mClipBox.r-x1, y2-y1, x2-x1);
                f3 = clipping_flags_y(y3, mClipBox);
                f4 = clipping_flags_y(y4, mClipBox);
                LineClipY(ras, mClipBox.l, y1, mClipBox.l, y3, f1, f3);
                LineClipY(ras, mClipBox.l, y3, mClipBox.r, y4, f3, f4);
                LineClipY(ras, mClipBox.r, y4, mClipBox.r, y2, f4, f2);
                break;

            case 12: // x1 < clip.l && x2 < clip.l
                LineClipY(ras, mClipBox.l, y1, mClipBox.l, y2, f1, f2);
                break;
            }
            mF1 = f2;
        }
        else
        {
            ras.Line(tConv::xi(mX1), tConv::yi(mY1),tConv::xi(x2),   tConv::yi(y2));
        }
        mX1 = x2;
        mY1 = y2;
    }

private:
    TRect        mClipBox;
    TCoord       mX1;
    TCoord       mY1;
    unsigned         mF1;
    bool             mClipping;
};

#if 0
    //---------------------------------------------------rasterizer_sl_no_clip
    class rasterizer_sl_no_clip
    {
    public:
        typedef ras_conv_int TConv;
        typedef int          TCoord;

        rasterizer_sl_no_clip() : mX1(0), mY1(0) {}

        void reset_clipping() {}
        void clip_box(TCoord x1, TCoord y1, TCoord x2, TCoord y2) {}
        void move_to(TCoord x1, TCoord y1) { mX1 = x1; mY1 = y1; }

        template<class Rasterizer>
        void line_to(Rasterizer& ras, TCoord x2, TCoord y2)
        {
            ras.line(mX1, mY1, x2, y2);
            mX1 = x2;
            mY1 = y2;
        }

    private:
        int mX1, mY1;
    };

#endif

typedef CRasterizerSLClip<ras_conv_int> TRasterizerSLClip_int;

//-----------------------------------------------------------------cell_aa
// A pixel cell. There're no constructors defined and it was done
// intentionally in order to avoid extra overhead when allocating an
// array of cells.
struct CCellAA
{
    int x;
    int y;
    int mCover;
    int mArea;

    void Initial()
    {
        x = 0x7FFFFFFF;
        y = 0x7FFFFFFF;
        mCover = 0;
        mArea  = 0;
    }

    void Style(const CCellAA&) {}

    int NotEqual(int ex, int ey, const CCellAA&) const
    {
        return (ex - x) | (ey - y);
    }
};


//==================================================rasterizer_scanline_aa
// Polygon rasterizer that is used to render filled polygons with
// high-quality Anti-Aliasing. Internally, by default, the class uses
// integer coordinates in format 24.8, i.e. 24 bits for integer part
// and 8 bits for fractional - see cPolySubpixelShift. This class can be
// used in the following  way:
//
// 1. filling_rule(filling_rule_e ft) - optional.
//
// 2. gamma() - optional.
//
// 3. reset()
//
// 4. MoveTo(x, y) / LineTo(x, y) - make the polygon. One can create
//    more than one contour, but each contour must consist of at least 3
//    vertices, i.e. MoveTo(x1, y1); LineTo(x2, y2); LineTo(x3, y3);
//    is the absolute minimum of vertices that define a triangle.
//    The algorithm does not check either the number of vertices nor
//    coincidence of their coordinates, but in the worst case it just
//    won't draw anything.
//    The orger of the vertices (clockwise or counterclockwise)
//    is important when using the non-zero filling rule (fill_non_zero).
//    In this case the vertex order of all the contours must be the same
//    if you want your intersecting polygons to be without "holes".
//    You actually can use different vertices order. If the contours do not
//    intersect each other the order is not important anyway. If they do,
//    contours with the same vertex order will be rendered without "holes"
//    while the intersecting contours with different orders will have "holes".
//
// filling_rule() and gamma() can be called anytime before "sweeping".
//------------------------------------------------------------------------
///\todo templatize gamma, create AIdentity as transform functor in buola/functors/transform and use as default
///also create ALookup which transforms based on a lookup table as it is here
template<class tClipper=TRasterizerSLClip_int>
class CScanlineRasterizer
{
    enum EStatus
    {
        STATUS_INITIAL,
        STATUS_MOVE_TO,
        STATUS_LINE_TO,
        STATUS_CLOSED
    };

public:
    typedef typename tClipper::TConv TConv;
    typedef CCellAA TCell;

    static const int cAAShift=8;
    static const int cAAScale=1<<cAAShift;
    static const int cAAMask=cAAScale-1;
    static const int cAAScale2=cAAScale*2;
    static const int cAAMask2=cAAScale2-1;

    CScanlineRasterizer()
    {
        for(int i=0;i<cAAScale;i++)
            mGamma[i]=i;
    }

    CScanlineRasterizer(const CScanlineRasterizer&)=delete;
    CScanlineRasterizer &operator=(const CScanlineRasterizer&)=delete;

    void Reset()
    {
        mOutline.Reset();
    }

    void ResetClipping()
    {
        Reset();
        mClipper.ResetClipping();
    }
    
    void ClipBox(double x1, double y1, double x2, double y2)
    {
        Reset();
        mClipper.ClipBox(TConv::upscale(x1), TConv::upscale(y1),
                        TConv::upscale(x2), TConv::upscale(y2));
    }

    void ClipBox(const CRect_d &pR)
    {
        Reset();
        mClipper.ClipBox(TConv::upscale(pR.l), TConv::upscale(pR.t),
                        TConv::upscale(pR.r), TConv::upscale(pR.b));
    }

    template<class tFunction>
    void Gamma(const tFunction& gamma_function)
    {
        for(int i = 0; i < cAAScale; i++)
        {
            mGamma[i] = gamma_function(double(i) / cAAMask) * cAAMask;
        }
    }

    unsigned ApplyGamma(unsigned cover) const
    {
        return mGamma[cover];
    }

    void MoveTo(const CPoint_d &pP)
    {
        if(mOutline.Sorted()) Reset();
        mClipper.MoveTo(TConv::upscale(pP.x),TConv::upscale(pP.y));
    }
    
    void LineTo(const CPoint_d &pP)
    {
        mClipper.LineTo(mOutline,TConv::upscale(pP.x),TConv::upscale(pP.y));
    }
    
    //--------------------------------------------------------------------
    int MinX() const { return mOutline.MinX(); }
    int MinY() const { return mOutline.MinY(); }
    int MaxX() const { return mOutline.MaxX(); }
    int MaxY() const { return mOutline.MaxY(); }

    //--------------------------------------------------------------------
    void Sort()
    {
        mOutline.SortCells();
    }

    bool RewindScanlines()
    {
        mOutline.SortCells();
        if(mOutline.TotalCells() == 0)
            return false;
        mScanY = mOutline.MinY();
        return true;
    }

    bool NavigateScanline(int y)
    {
        mOutline.SortCells();
        if(mOutline.TotalCells() == 0 ||
            y < mOutline.MinY() ||
            y > mOutline.MaxY())
        {
            return false;
        }
        mScanY = y;
        return true;
    }

    //--------------------------------------------------------------------
    unsigned CalculateAlpha(int pArea) const
    {
        int lCover=pArea>>(cPolySubpixelShift*2+1-cAAShift);

        if(lCover<0) lCover=-lCover;
        if(0) /*mFillingRule == FILL_EVEN_ODD*////\todo
        {
            lCover&=cAAMask2;
            if(lCover>cAAScale)
                lCover=cAAScale2-lCover;
        }
        if(lCover>cAAMask) lCover=cAAMask;
        return mGamma[lCover];
    }

    //--------------------------------------------------------------------
    template<class tScanline>
    bool SweepScanline(tScanline& sl)
    {
        for(;;)
        {
            if(mScanY > mOutline.MaxY()) return false;
            sl.ResetSpans();
            unsigned num_cells = mOutline.ScanlineCellCount(mScanY);
            const CCellAA* const* cells = mOutline.ScanlineCells(mScanY);
            int cover = 0;

            while(num_cells)
            {
                const CCellAA* cur_cell = *cells;
                int x    = cur_cell->x;
                int area = cur_cell->mArea;
                unsigned alpha;

                cover += cur_cell->mCover;

                //accumulate all cells with the same X
                while(--num_cells)
                {
                    cur_cell = *++cells;
                    if(cur_cell->x != x) break;
                    area  += cur_cell->mArea;
                    cover += cur_cell->mCover;
                }

                if(area)
                {
                    alpha = CalculateAlpha((cover << (cPolySubpixelShift + 1)) - area);
                    if(alpha)
                    {
                        sl.AddCell(x, alpha);
                    }
                    x++;
                }

                if(num_cells && cur_cell->x > x)
                {
                    alpha = CalculateAlpha(cover << (cPolySubpixelShift + 1));
                    if(alpha)
                    {
                        sl.AddSpan(x, cur_cell->x - x, alpha);
                    }
                }
            }

            if(sl.NumSpans()) break;
            ++mScanY;
        }

        sl.Finalize(mScanY);
        ++mScanY;
        return true;
    }

    template<typename tRenderer>
    void RenderScanlines(tRenderer &pR,const typename tRenderer::TPixel &pPixel)
    {
        int lNumSpans=0;
        for(mScanY=mOutline.MinY();mScanY<=mOutline.MaxY();++mScanY)
        {
            auto lIt=pR.Row(mScanY).begin();
            size_t lNumCells=mOutline.ScanlineCellCount(mScanY);
            const TCell* const* lCells=mOutline.ScanlineCells(mScanY);
            int lCover = 0;

            while(lNumCells)
            {
                const TCell *lCurCell=*lCells;
                int x    = lCurCell->x;
                int lArea = lCurCell->mArea;
                unsigned lAlpha;

                lCover += lCurCell->mCover;

                //accumulate all cells with the same X
                while(--lNumCells)
                {
                    lCurCell = *++lCells;
                    if(lCurCell->x != x) break;
                    lArea  += lCurCell->mArea;
                    lCover += lCurCell->mCover;
                }

                if(lArea)
                {
                    lAlpha=CalculateAlpha((lCover << (cPolySubpixelShift + 1)) - lArea);
                    if(lAlpha)
                    {
                        lIt[x].Blend(pPixel,lAlpha);
                        //pR(x,mScanY).Blend(pPixel,lAlpha);
                    }
                    x++;
                }

                if(lNumCells && lCurCell->x > x)
                {
                    lAlpha=CalculateAlpha(lCover << (cPolySubpixelShift + 1));
                    if(lAlpha)
                    {
                        blend_hline(pR,x,mScanY,lCurCell->x-x,pPixel,lAlpha);
                        lNumSpans++;
//                        sl.AddSpan(x, cur_cell->x - x, alpha);
                    }
                }
            }
        }
    }
    
    ///\todo check possible optimizations, such as calling a different function when the cover is known to not be 255, or
    ///when it is known to be, or to not have to calculate the iterator every time within the same y. Maybe a StartRow/EndRow
    ///that allows the renderer to prepare for a certain y.
    ///also try passing a whole array of spans
    template<typename tRenderer>
    void RenderScanlines(tRenderer &pRenderer)
    {
        for(int y=mOutline.MinY();y<=mOutline.MaxY();y++)
        {
            size_t lNumCells=mOutline.ScanlineCellCount(y);
            const TCell* const* lCells=mOutline.ScanlineCells(y);
            int lCover = 0;

            while(lNumCells)
            {
                const TCell *lCurCell=*lCells;
                int x=lCurCell->x;
                int lArea=lCurCell->mArea;

                lCover+=lCurCell->mCover;

                //accumulate all cells with the same X
                while(--lNumCells)
                {
                    lCurCell=*++lCells;
                    if(lCurCell->x!=x) break;
                    lArea+=lCurCell->mArea;
                    lCover+=lCurCell->mCover;
                }

                if(lArea)
                {
                    int lAlpha=CalculateAlpha((lCover<<(cPolySubpixelShift+1))-lArea);
                    if(lAlpha)
                        pRenderer.RenderPixel(x,y,lAlpha);
                    x++;
                }

                if(lNumCells&&lCurCell->x>x)
                {
                    int lAlpha=CalculateAlpha(lCover<<(cPolySubpixelShift+1));
                    if(lAlpha)
                        pRenderer.RenderHLine(x,y,lCurCell->x-x,lAlpha);
                }
            }
        }
    }

    template<typename tRenderer>
    void RenderScanlinesRows(tRenderer &pRenderer)
    {
        for(int y=mOutline.MinY();y<=mOutline.MaxY();y++)
        {
            pRenderer.StartRow(y);
            size_t lNumCells=mOutline.ScanlineCellCount(y);
            const TCell* const* lCells=mOutline.ScanlineCells(y);
            int lCover = 0;

            while(lNumCells)
            {
                const TCell *lCurCell=*lCells;
                int x=lCurCell->x;
                int lArea=lCurCell->mArea;

                lCover+=lCurCell->mCover;

                //accumulate all cells with the same X
                while(--lNumCells)
                {
                    lCurCell=*++lCells;
                    if(lCurCell->x!=x) break;
                    lArea+=lCurCell->mArea;
                    lCover+=lCurCell->mCover;
                }

                if(lArea)
                {
                    int lAlpha=CalculateAlpha((lCover<<(cPolySubpixelShift+1))-lArea);
                    if(lAlpha)
                        pRenderer.RenderPixel(x,lAlpha);
                    x++;
                }

                if(lNumCells&&lCurCell->x>x)
                {
                    int lAlpha=CalculateAlpha(lCover<<(cPolySubpixelShift+1));
                    if(lAlpha)
                        pRenderer.RenderHLine(x,lCurCell->x-x,lAlpha);
                }
            }
        }
    }

    template<typename tRenderer>
    void RenderScanlinesSpans(tRenderer &pRenderer)
    {
        for(int y=mOutline.MinY();y<=mOutline.MaxY();y++)
        {
            int lXs[1000];
            int lLens[1000];
            uint8_t lAlphas[1000];
            int n=0;
            
            size_t lNumCells=mOutline.ScanlineCellCount(y);
            const TCell* const* lCells=mOutline.ScanlineCells(y);
            int lCover = 0;

            while(lNumCells)
            {
                const TCell *lCurCell=*lCells;
                int x=lCurCell->x;
                int lArea=lCurCell->mArea;

                lCover+=lCurCell->mCover;

                //accumulate all cells with the same X
                while(--lNumCells)
                {
                    lCurCell=*++lCells;
                    if(lCurCell->x!=x) break;
                    lArea+=lCurCell->mArea;
                    lCover+=lCurCell->mCover;
                }

                if(lArea)
                {
                    int lAlpha=CalculateAlpha((lCover<<(cPolySubpixelShift+1))-lArea);
                    if(lAlpha)
                    {
                        lXs[n]=x;
                        lLens[n]=1;
                        lAlphas[n]=lAlpha;
                        n++;
                    }
                    x++;
                }

                if(lNumCells&&lCurCell->x>x)
                {
                    int lAlpha=CalculateAlpha(lCover<<(cPolySubpixelShift+1));
                    if(lAlpha)
                    {
                        lXs[n]=x;
                        lLens[n]=lCurCell->x-x;
                        lAlphas[n]=lAlpha;
                        n++;
                    }
                }
            }
            if(n)
            {
                pRenderer.RenderSpans(y,lXs,lLens,lAlphas,n);
            }
        }
    }

    void RenderToScanlines(CScanlines &pSL)
    {
        pSL=pSL;
        pSL.Reset(mOutline.MinY(),mOutline.MaxY());
        
        for(int y=mOutline.MinY();y<=mOutline.MaxY();y++)
        {
            pSL.StartLine(y);
            size_t lNumCells=mOutline.ScanlineCellCount(y);
            const TCell* const* lCells=mOutline.ScanlineCells(y);
            int lCover = 0;

            while(lNumCells)
            {
                const TCell *lCurCell=*lCells;
                int x=lCurCell->x;
                int lArea=lCurCell->mArea;

                lCover+=lCurCell->mCover;

                //accumulate all cells with the same X
                while(--lNumCells)
                {
                    lCurCell=*++lCells;
                    if(lCurCell->x!=x) break;
                    lArea+=lCurCell->mArea;
                    lCover+=lCurCell->mCover;
                }

                if(lArea)
                {
                    int lAlpha=CalculateAlpha((lCover<<(cPolySubpixelShift+1))-lArea);
                    if(lAlpha)
                        pSL.AddSpan(x,1,lAlpha);
                    x++;
                }

                if(lNumCells&&lCurCell->x>x)
                {
                    int lAlpha=CalculateAlpha(lCover<<(cPolySubpixelShift+1));
                    if(lAlpha)
                        pSL.AddSpan(x,lCurCell->x-x,lAlpha);
                }
            }
            pSL.EndLine(y);
        }
    }

//    bool HitTest(int tx, int ty);


private:
    CCellRasterizer<TCell> mOutline;
    tClipper mClipper;
    int            mGamma[cAAScale];
    int            mScanY;
};

#if 0
template<class Clip>
bool rasterizer_scanline_aa<Clip>::hit_test(int tx, int ty)
{
    if(!navigate_scanline(ty)) return false;
    scanline_hit_test sl(tx);
    sweep_scanline(sl);
    return sl.hit();
}
#endif

template<class tRasterizer,class tScanline,class tRenderer>
void render_scanlines_aa_solid(tRasterizer &pRas,tScanline& pSL,tRenderer &pRen,const typename tRenderer::TPixel &pPixel)
{
    if(pRas.RewindScanlines())
    {
        pSL.Reset(pRas.MinX(),pRas.MaxX());
        while(pRas.SweepScanline(pSL))
        {
            //render_scanline_aa_solid(sl, ren, ren_color);

            // This code is equivalent to the above call (copy/paste).
            // It's just a "manual" optimization for old compilers,
            // like Microsoft Visual C++ v6.0
            //-------------------------------
            int y = pSL.y();
            unsigned lNumSpans = pSL.NumSpans();
            auto lSpan=pSL.begin();

            for(;;)
            {
                int x = lSpan->x;
                if(lSpan->mLen > 0)
                {
                    blend_hspan(pRen,x,y,lSpan->mLen,pPixel,lSpan->mCovers);
                }
                else
                {
                    blend_hline(pRen,x,y,-lSpan->mLen,pPixel,*lSpan->mCovers);
                }
                if(--lNumSpans == 0) break;
                ++lSpan;
            }
        }
    }
}

template<class Rasterizer, class Scanline, class Renderer>
void render_scanlines(Rasterizer& ras, Scanline& sl, Renderer& ren)
{
    if(ras.RewindScanlines())
    {
        sl.Reset(ras.MinX(), ras.MaxX());
        ren.Prepare();
        while(ras.SweepScanline(sl))
        {
            ren.Render(sl);
        }
    }
}

/*namespace image*/ } /*namespace buola*/ }

#endif
